<?php
/** Rho logging framework.
*
* This file will normally be loaded automatically on demand.
* It is ok to require_once this file if you know you need logging.
*
* @package Rho
* @author Pete
* @version 0.1
* @license http://www.opensource.org/licenses/apache2.0.php Apache License, Version 2.0.*/

/** Class that provides static methods for logging.
* @package Rho */
final class Rho_Log{

  /** Prevents instances.*/
    private function __construct(){
  }
  
  /** Logs a message.
  * How the message is logged depends on the config setting of log_$type (e.g. log_errors ).
  * Uses bitwise comparison against the logging constants,
  * so a message can be logged in multiple ways (ala PHP's error constants).
  * @param string $type One of errors, critical_errors, alerts or info.
  * @param int $code A message code.
  * @param string $message The message to log.  This will be prefixed with it's type (error, alert, etc.). */
  private static function _log($type='errors', $code, $message){
    $e=Rho::conf('log.'.$type);
    if(! $e ) return; # no logging for this type
    switch($type){
      case 'errrors' : $prefix='ERROR #'; break;
      case 'critical_errors' : $prefix='ERROR CRITICAL #'; break;
      case 'alerts' : $prefix='ALERT #'; break;
      case 'info' : $prefix='INFO'; break;
      default: $prefix='ERROR #';
    }
    $msg=$prefix.$code.' '.$message;
    
    if($e & Rho::LOG_SYSTEM){
      @error_log($msg, 0); # system log
    }
    
     if($e & Rho::LOG_FILE){
      $logfile=Rho::$APP_PATH.'logs/'. Rho::nowDate(). '.log';
      @error_log(Rho::nowTime().' '.$msg."\n", 3, $logfile); # app log file
    }
    
    if($e & Rho::LOG_EMAIL){
      @error_log($msg, 4); # send an email
    }
    
    if($e & Rho::LOG_SAPI){
      @error_log($msg, 4); # sapi handler
    }
    
  }
  
  /** Logs an exception.
  * Will attempt to send a HTTP error response code (e.g. 500) if possible.
  * IF $e is an instance of Rho_Critical_Exception, script execution will be terminated.
  * @param Exception $e */
  public static function log(Exception $e){
    // try to send a error header
    self::_exceptionHeaders($e);
    // is it critical ?
    if($e instanceof Rho_Critical_Exception){
      self::critical($e->getCode(), $e->getMessage() );
      if(Rho::_getController()!=NULL){ # do we have a controller ?
        try{
          Rho::_getController()->_onError($e);
        }
        catch(Exception $e){
          //swallow
        }
      }
      die(($e->getCode() % 254) +1);
    }
    else {
      self::error($e->getCode(), $e->getMessage() );
    }
  }
  
  /** Logs an error.
  * How the error is logged depends on the config setting 'log_errors'.
  * @param int $code An error code.
  * @param string $message The error message. */
  public static function error($code=1000, $message='General Error'){
    $debug=self::backtrace(debug_backtrace());
    self::_log('errors', $code, $message."\n$debug");
  }
  
  /** Logs a critical error.
  * How the error is logged depends on the config setting 'log_errors.
  * @param int $code An error code.
  * @param string $message The error message. */
  public static function critical($code=5000, $message='Critical Error'){
    $debug=self::backtrace(debug_backtrace());
    self::_log('critical_errors', $code, $message."\nReferer: ".Rho::ifset($_SERVER, 'HTTP_REFERER', 'None').
    "\n".$debug);
  }
  
  /** Logs an informational message.
  * Info is considered non-essential, and may well be ignored.
  * Useful for recording debugging info.
  * How the message is logged depends on the config setting 'log_info'.
  * @param string $message The info message.*/
  public static function info($message){
    if(! Rho::conf('log.info') ) return;
    self::_log('info', '', $message);
  }
  
  /** Logs an alert message.
  * Alert messages have higher priority than info, but lower than error.
  * Useful for logging security events, such as when a user request a password reset.
  * The visitors IP address will be prepended to the message.
  * How the it is logged depends on the config setting 'log_alerts'.
  * @param int $code An alertcode.
  * @param string $message The alert message. */
  public static function alert($message, $code=3000){
    self::_log('alerts', $code, $message."\nIP address: ".self::getIP());
  }
  
  /** @ignore*/
  public static function _exceptionHeaders(Exception & $e){
    if(! headers_sent() ){
      if($e instanceof Rho_Exception){
        Rho::statusHeader($e->getHttpCode(), $e->getHttpText());
      }
      else {
        Rho::statusHeader(500, 'Internal Server Error');
      }
    }
  }
  
  /** Produces a formatted backtrace.
  * @param array $debug_backtrace The result of a call to PHP's debug_backtrace().
  * @return string .*/
  public static function backtrace($debug_backtrace){
    if(count($debug_backtrace)<2) return '';
    $result='';
    array_shift($debug_backtrace);
    $later=FALSE;
    foreach($debug_backtrace as $trace) {
      if($later){
        $result .= "\n";
      }
      else{
        $later=TRUE;
      }
      if( isset($trace['class']) ){
        $result .= '  '.$trace['class'].Rho::ifset($trace, 'type' ,'?').Rho::ifset($trace, 'function' ,' ?unknown?');
      }
      else{
        $result .= '  '.Rho::ifset($trace, 'function', '').'() in '.Rho::ifset($trace, 'file' ,' unknown file').' line #'.Rho::ifset($trace, 'line', 0);
      }
    }
    return $result;
  }
}